﻿using QxFramework.Core;
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;

namespace ERAnimation
{
    public class ERAnimatorController : MonoBehaviour
    {
        /// <summary>
        /// 动画状态队列的最大储存数量
        /// </summary>
        private const int QueueStateCount = 10;
        private float _time;
        private bool _enableForceBreak;
        private bool _disableStateTransfer;
        public ERAnimationState _currentState;
        public List<ERAnimationState> states = new List<ERAnimationState>();

        /// <summary>
        /// 当前状态
        /// </summary>
        public ERAnimationState CurrentState
        {
            get
            {
                return _currentState;
            }
            private set
            {
                _currentState = value;
            }
        }

        public ERAnimationState DefaultState
        {
            get
            {
                foreach (var state in states)
                {
                    if (state.isDefaultState)
                    {
                        return state;
                    }
                }
                return null;
            }
        }

        /// <summary>
        /// 曾经播放过的动画状态队列
        /// </summary>
        public readonly Queue<ERAnimationState> lastStates = new Queue<ERAnimationState>();

        public Animator Animator => GetComponent<Animator>();

        /// <summary>
        /// 当该值为true时，会禁用动画状态的切换
        /// </summary>
        public bool DisableStateTransfer
        {
            get => _disableStateTransfer;
            set => _disableStateTransfer = value;
        }

        /// <summary>
        /// 上一个动画状态
        /// </summary>
        public ERAnimationState LastState => lastStates.Peek();

        /// <summary>
        /// 切换指定名称的动画状态。你不应再某个退出类的函数里执行这个方法。
        /// </summary>
        /// <param name="animation">要播放的动画状态</param>
        public void Play(ERAnimationState state)
        {
            if (Animator != null && !_disableStateTransfer)
            {
                if (CurrentState != null)
                {
                    foreach (var e in CurrentState.events)
                    {
                        if (e.isPlaying)
                        {
                            e.OnQuit();
                            e.OnLeave();
                        }
                        e.OnStateLeave();
                    }
                    lastStates.Enqueue(CurrentState);
                    if (lastStates.Count > QueueStateCount)
                    {
                        lastStates.Dequeue();
                    }
                }
                CurrentState = state;
                Animator.Play(CurrentState.linkedState);
                _time = 0;
                Animator.Update(0);
                foreach (var e in CurrentState.events)
                {
                    e.OnStateEnter();
                    if (e.startTime == 0)
                    {
                        e.OnEnter();
                    }
                }
            }
            else
            {
                Debug.LogError(this.GetType().Name + "的动画机未赋值！\n");
                throw new NullReferenceException();
            }
        }

        /// <summary>
        /// 切换指定名称的动画状态。你不应再某个退出类的函数里执行这个方法。
        /// </summary>
        /// <param name="animation">要播放的动画状态对应的动画名称</param>
        public void Play(string animation, bool replay = true)
        {
            if (!replay && Animator.GetCurrentAnimatorStateInfo(0).IsName(animation))
            {
                return;
            }
            ERAnimationState state = states.Find((a) => { return a.linkedState == animation; });
            if (state != null)
            {
                Play(state);
            }
            else
            {
                Debug.LogError("未找到" + animation + "对应的动画状态");
            }
        }

        /// <summary>
        /// 执行后会使接下来的循环终止
        /// </summary>
        public void EnableForceBreak()
        {
            _enableForceBreak = true;
        }

        #region Update Animation State
        private void Awake()
        {
            List<ERAnimationState> cloneStates = new List<ERAnimationState>();
            foreach (var state in states)
            {
                var cloneState = Instantiate(state);
                //cloneState.Init(this, state.linkedAnimation);
                List<ERAnimationEvent> cloneEvents = new List<ERAnimationEvent>();
                foreach (var e in state.events)
                {
                    var cloneEvent = Instantiate(e);
                    List<ERAnimationArg> cloneArgs = new List<ERAnimationArg>();
                    foreach (var a in e.args)
                    {
                        var cloneArg = Instantiate(a);
                        cloneArgs.Add(cloneArg);
                    }
                    cloneEvent.linkedState = cloneState;
                    cloneEvent.args = cloneArgs;
                    //cloneEvent.Init();
                    cloneEvents.Add(cloneEvent);
                }
                cloneState.events = cloneEvents;
                cloneState.events.Sort((a, b) => {
                    if (a.line > b.line)
                    {
                        return 1;
                    }
                    else if (a.line == b.line)
                    {
                        return 0;
                    }
                    else
                    {
                        return -1;
                    }
                });
                cloneStates.Add(cloneState);
            }
            states = cloneStates;
            foreach (var state in states)
            {
                state.Init(this, state.linkedState, state.linkedAnimation);
                foreach (var e in state.events)
                {
                    e.Init();
                }
            }/*
            _enableForceBreak = false; _disableStateTransfer = false;
            Play(DefaultState);*/
        }

        private void OnEnable()
        {
            _enableForceBreak = false; _disableStateTransfer = false;
        }

        private void LateUpdate()
        {
            //Debug.Log("LateUpdate:" + Animator.GetCurrentAnimatorStateInfo(0).normalizedTime % 1 * Animator.GetCurrentAnimatorStateInfo(0).length * Animator.speed);
            if (CurrentState != null)
            {
                foreach (var e in CurrentState.events)
                {
                    if (_enableForceBreak)
                    {
                        _time = 0;
                        break;
                    }
                    if (Animator.GetCurrentAnimatorStateInfo(0).normalizedTime % 1 * Animator.GetCurrentAnimatorStateInfo(0).length * Animator.speed < _time && e.isPlaying)
                    {
                        e.OnStateReplay();
                        e.OnLeave();
                    }
                    if (e.isPlaying)
                    {
                        if (_time < e.startTime)
                        {
                            e.OnLeave();
                        }
                    }
                    if (!e.isPlaying)
                    {
                        if (!e.isPlayed && _time >= e.startTime)
                        {
                            e.OnEnter();
                        }
                    }
                    else
                    {
                        if (_time >= e.endTime)
                        {
                            e.OnLeave();
                        }
                    }
                    if (!_enableForceBreak)
                    {
                        e.Update();
                    }
                }
                _enableForceBreak = false;
            }
            else
            {
                Debug.Log("wtf");
            }
        }

        private void Update()
        {
            if (CurrentState == null)
            {
                Play(DefaultState);
            }
            //Debug.Log("Update:" + Animator.GetCurrentAnimatorStateInfo(0).normalizedTime % 1 * Animator.GetCurrentAnimatorStateInfo(0).length * Animator.speed);
            _time = Animator.GetCurrentAnimatorStateInfo(0).normalizedTime % 1 * Animator.GetCurrentAnimatorStateInfo(0).length * Animator.speed;
        }

        public void FixedUpdate()
        {
            if (CurrentState != null)
            {
                foreach (var e in CurrentState.events)
                {
                    e.FixedUpdate();
                }
            }
        }
        #endregion
    }
}